package com.superbytecode.stencil.wrapper;

import com.superbytecode.stencil.entities.ConnectionObj;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.jdbc.SqlRunner;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.TextSqlNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

/**
 * All rights Reserved, Designed By www.super-master.com
 *
 * @ProjectName: code-stencil
 * @Package: com.superbytecode.stencil.structure
 * @ClassName: SQLScriptExecutor
 * @Description: [sql脚本执行器]
 * <p> </p>
 * @Author: [Lance Ting]
 * @Date: 2024/10/12 14:50
 * @Version: V1.0
 * @Copyright: 2024 www.super-master.cn Inc. All rights reserved.
 * TODO: 注意,本文件Lance Ting所作,如果转载或使用请标明具体出处!
 **/
@Slf4j
public class SQLScriptExecutor {
    /**
     * 数据库连接配置
     */
    final ConnectionObj connectionObj;

    /**
     * 初始化执行器
     *
     * @param connectionObj 数据库连接配置
     * @return 执行器
     */
    public static SQLScriptExecutor builder(ConnectionObj connectionObj) {
        return new SQLScriptExecutor(connectionObj);
    }

    /**
     * 运行sql
     *
     * @param sqlScript sql脚本
     * @return 直接结果
     */
    public List<Map<String, Object>> runSqlScript(String sqlScript) {
        return runSqlScript(sqlScript, null);
    }

    /**
     * 运行sql
     *
     * @param sqlScript sql脚本
     * @param params    参数列表
     * @return 直接结果
     */
    public List<Map<String, Object>> runSqlScript(String sqlScript, Map<String, Object> params) {
        // 获取数据库资源
        Connection connection = null;
        String sqlStatementParams = "";
        try {
            // 获取数据库资源
            DataSource dataSource = DatabaseManager.obtainDatasource(this.connectionObj);
            // 构建sql脚本执行参数
            sqlStatementParams = bindSQLStatementParams(dataSource, sqlScript, params);
            String[] sqlScripts = sqlStatementParams.split(";");
            // 获取数据库连接
            connection = DatabaseManager.obtainConnection(this.connectionObj);
            SqlRunner sqlRunner = this.bindSqlRunner(connection);
            //
            int scriptCount = sqlScripts.length;// 可执行脚本个数
            if (scriptCount == 1) {
                return sqlRunner.selectAll(sqlScripts[0]);
            }
            for (int i = 0; i < scriptCount - 1; i++) {
                sqlRunner.run(sqlScripts[i]);
            }
            return sqlRunner.selectAll(sqlScripts[scriptCount - 1]);
        } catch (Exception e) {
            log.error("run sql script [{}] had error.", sqlStatementParams, e);
            throw new RuntimeException("run sql script [" + sqlStatementParams + "] had error");
        }
    }

    /**
     * 关闭资源
     *
     * @param connection 数据库连接
     */
    public void closeConnection(Connection connection) {
        if (null == connection) return;
        try {
            connection.close();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    // =================================================================================================================

    /**
     * 绑定SQL脚本执行器
     *
     * @param connection 数据库连接
     * @return sql脚本执行器
     */
    private SqlRunner bindSqlRunner(Connection connection) {
        return new SqlRunner(connection);
    }

    /**
     * 绑定SQL脚本参数
     *
     * @param datasource 数据库资源对象
     * @param sqlScript  SQL脚本
     * @param params     参数
     * @return 参数
     */
    private String bindSQLStatementParams(DataSource datasource, String sqlScript, Map<String, Object> params) {
        Configuration configuration = this.bindDataEnvironment(datasource);
        TextSqlNode node = new TextSqlNode(sqlScript);
        DynamicSqlSource dynamicSqlSource = new DynamicSqlSource(configuration, node);
        BoundSql boundSql = dynamicSqlSource.getBoundSql(params);
        return boundSql.getSql();
    }

    /**
     * 绑定环境配置
     *
     * @param datasource 数据源信息
     * @return 配置信息
     */
    private Configuration bindDataEnvironment(DataSource datasource) {
        // 设置mybatis事务
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        // 设置环境
        Environment environment = new Environment("development", transactionFactory, datasource);
        return new Configuration(environment);
    }

    // =================================================================================================================
    private SQLScriptExecutor(ConnectionObj connectionObj) {
        this.connectionObj = connectionObj;
    }
}